//  This file is part of Swordbible - http://www.swordwarrior.net
//  Copyright (C) 2003-2009 Manu Mital mital.manu@gmail.com

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2, or (at your option)
//  any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


// VerseLists.cpp : implementation file
//

#include <stdafx.h>
#include "SwordBible.h"
#include "VerseLists.h"
#include "CreateList.h"
#include "BiblePage.h"
#include <odbcinst.h> 
#include <afxdb.h> 
#include "ColorMgr.h"
#include "Remove.h"
#include "BibleForm.h"
#include "SaveVerseRange.h"
#include "Search.h"

// static variables
int CVerseLists::active = 0;


// CVerseLists dialog

IMPLEMENT_DYNAMIC(CVerseLists, CDialog)

CVerseLists::CVerseLists(CWnd* pParent /*=NULL*/)
	: CDialog(CVerseLists::IDD, pParent)
{
	

}

CVerseLists::~CVerseLists()
{
}

void CVerseLists::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_RICHEDIT_MEM, m_richCtrl);
	DDX_Control(pDX, IDC_VERSELISTS_TREE, treeCtrl);
}


BEGIN_MESSAGE_MAP(CVerseLists, CDialog)
	ON_COMMAND(ID_VERSELISTS_PRINT, OnPrint)
	ON_COMMAND(ID_VERSELISTS_EXIT, HideVerseLists)
	ON_COMMAND(ID_VERSELISTS_NEXT, OnNext)
	ON_COMMAND(ID_VERSELISTS_PREVIOUS, OnPrevious)
  ON_COMMAND(ID_VERSELISTS_EDIT, &CVerseLists::OnAddverse)	
	ON_COMMAND(ID_EDIT_REMOVELIST, &CVerseLists::OnDeleteList)
	ON_COMMAND(ID_EDIT_ADDLIST, &CVerseLists::OnMakeList)
	ON_COMMAND(ID_EDIT_REMOVEVERSE, &CVerseLists::OnDeleteVerse)
	ON_COMMAND_RANGE(ID_BIBLEVERSION_NAMES,ID_BIBLEVERSION_NAMES + nBibles , OnChangeVersion)
	ON_COMMAND(ID_VERSELISTS_BIBLEVERSIONS, OnToolbarBibleVersions)
	ON_BN_CLICKED(ID_ADDVERSE, &CVerseLists::OnAddverse)	
	ON_NOTIFY(TVN_SELCHANGED, IDC_VERSELISTS_TREE, &CVerseLists::OnTreeChangeSel)
	ON_WM_SIZE()
END_MESSAGE_MAP()


// CVerseLists message handlers

// adds a verse to the currently selected list
void CVerseLists::OnAddverse()
{
	 CVersePicker dlg;  //open a dialog from which user can select a verse key
     if(dlg.DoModal() == IDOK) 
	 { 
	     // Add verse to the tree and the database
	 
		HTREEITEM selected = treeCtrl.GetSelectedItem();  // Get the selected Tree Control item
		CString sel_text = treeCtrl.GetItemText(selected);  // Get the text of the selected item

		if(sel_text.GetLength() < 3 ) return; // list name must be between 3 and 25 characters

	    SaveList(sel_text, dlg.key, TRUE ); //  appended stuff to the list
		
		
		DisplayText();  // fill the rich edit control
	 }
		

	
}

// toAppendList is added to ExistingList, checking to make sure there are no duplicate
// verses in the list

void CVerseLists:: AppendData(CString& ExistingList, CString toAppendList)
{

	 sword::VerseKey vk;
	 sword::ListKey lk_1, lk_2;
   lk_1 = vk.ParseVerseList((CStringA) ExistingList, vk , TRUE);
	 lk_2 = vk.ParseVerseList((CStringA) toAppendList,vk, TRUE);
		
		for (lk_2 = sword::TOP; !lk_2.Error(); lk_2++) 
		{
			BOOL found = FALSE;
			lk_1 = sword::TOP;

			while(!lk_1.Error() && (found == FALSE))
			{
                 if(lk_1 == lk_2) found = TRUE;
				 lk_1++;
			}

		 if(found == FALSE)	 lk_1.add(lk_2);
		
		}  

    // append
		ExistingList = _T("");
		for (lk_1 = sword::TOP; !lk_1.Error(); lk_1++) 
			ExistingList = ExistingList + (CString) lk_1 + _T(" ;") ;

}



/* save the verses in the specified list to the Data.mdb database */
void CVerseLists::SaveList(CString listname, CString listdata, BOOL append) 
{
	// TODO: Add your command handler code here
		if(IsAppData == FALSE)
		return ;    // no point in continuing if database DNE

	try {
		// Update the current record
		if(m_piAdoRecordBinding)
      m_piAdoRecordBinding->Update(&m_rsRecSet);

	 if(VARIANT_FALSE == m_pRs->BOF)  m_pRs->MoveFirst();  // move to the first record

	CString tofind;
	CString temp = listname;
	temp.Replace(_T("'"),  _T("''")  );
	tofind.Format(_T("ListName='%s'"), temp);
	m_pRs->Find(tofind.AllocSysString() , 0, ADOCG::adSearchForward);    

	RefreshBoundData();



	if(list_name.CompareNoCase(listname) == 0)  
	{
		if(append) AppendData(data, listdata);  // append verses in listdata to existing verses in data, 
		                             // avoiding duplicaion

		else data = listdata;            // over-write mode
		UpdateBoundData();	
		m_piAdoRecordBinding->Update(&m_rsRecSet);
		
	}
	// if the record is not there, lets add a new record
	else {		
	AddNew();

	// lets copy the verse code and string into temp variable of THIS class
	list_name = listname;
	data = listdata;
	UpdateBoundData();
	m_piAdoRecordBinding->Update(&m_rsRecSet);


	
	}

	}

	   // Any errors?
     catch (_com_error &e)
     {         // Generate an error message
         GenerateError(e.Error(), e.Description());
	 }

	
}

BOOL CVerseLists::OnInitDialog()
{
	CDialog::OnInitDialog();



	//set the icon for the dialog
	SetIcon(::AfxGetApp()->LoadIcon(IDR_MAINFRAME), TRUE);

	//load the font and construct the RTF
	m_richCtrl.SetBackgroundColor(FALSE, CColorMgr::RTFtoCOLORREF(CBiblePage::font.bg_color) );
	m_richCtrl.SendMessage(EM_EXLIMITTEXT,0,-1);

	active = CBiblePage::active;
	pModuleActive = CSwordBibleDoc::pRTFBibleModule[active];

	/*********************** customizing the look and feel of the tree control *******************************/

	//SetRedraw(FALSE);
	
	HICON icon_folder = ::AfxGetApp()->LoadIcon(IDI_ICON_FOLDER);
	

	m_image.Create(16,16, ILC_COLORDDB ,0,1);
	m_image.SetBkColor(RGB(255,255,255));
	m_image.Add(icon_folder);


	treeCtrl.SetImageList(&m_image, TVSIL_NORMAL );

	// add background effect
	treeCtrl.SetBkColor( RGB(245,255,255));

	treeCtrl.ModifyStyle(NULL, treeCtrl.GetStyle() |TVS_HASLINES |TVS_LINESATROOT|TVS_SHOWSELALWAYS|
			TVS_DISABLEDRAGDROP );

	//SetRedraw(TRUE);

	 
	/*********************** DATABASE stuff: the VerseLists uses different database file as the notes page ****/
	 IsAppData = TRUE ;   // start by assuming that the notes database file exists

	 GetAppDirectory(path);
	path += _T("\\Data.mdb");


	if(GetFileAttributes(path) == -1)
	 { CreateDatabaseX();
		// has the file been created successfully ? Check in the next step
	    
	}

	if(GetFileAttributes(path) == -1)
	 IsAppData = FALSE;

	 CreateDB();

	TCHAR * pChar = new TCHAR[400];
	memset(pChar, 0, 400);
	_stprintf(pChar, _T("Provider='Microsoft.JET.OLEDB.4.0';Data source = %s"),path);
	m_strConnection = pChar;
    m_strCmdText = _T("select * from VerseListsTable");
	delete [] pChar;


     // Initialize the Recordset and binding pointers
     m_pRs = NULL;
     m_piAdoRecordBinding = NULL;


	 // Initialize the COM environment
     ::CoInitialize(NULL);

	 try {
	 // Create the record set object
     m_pRs.CreateInstance(__uuidof(Recordset));


	 // Open the record set object
     m_pRs->Open((LPCTSTR)m_strCmdText, (LPCTSTR)m_strConnection, 
            adOpenDynamic, adLockOptimistic, adCmdUnknown);

	
	 // Get a pointer to the record binding interface
         if (FAILED(m_pRs->QueryInterface(__uuidof(IADORecordBinding),
                 (LPVOID *)&m_piAdoRecordBinding)))
             _com_issue_error(E_NOINTERFACE);


		  // Bind the record class to the record set
        m_piAdoRecordBinding->BindToRecordset(&m_rsRecSet);

		RefreshBoundData();


		// if there are zero records, add a record
		if (m_pRs->BOF && m_pRs->EndOfFile)
		{
			
			m_pRs->AddNew();
			m_pRs->Fields->GetItem(long(0))->PutValue(_T("0")); 
			m_pRs->Fields->GetItem(long(1))->PutValue(_T("0")); 
			m_pRs->Update();
		}


		PopulateTree();

     }


     // Any errors?
     catch (_com_error &e)
     {
         // Display the error
         GenerateError(e.Error(), e.Description());
     }

	 AppendBibleVersions(); // add the different bible versions to the menu

	 LoadToolbar();


	 SetWindowPos(NULL,250,250, 700,600, SWP_NOZORDER | SWP_NOACTIVATE);

	

	return TRUE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}



BOOL CVerseLists:: CreateDB() 

{
	// programmatically configure a user's machine to use access database
	TCHAR* szDesc;
	int mlen;
	szDesc = new TCHAR[256];

	CString csPath = path;

	_stprintf(szDesc,_T("DSN=%s; DESCRIPTION=%s; DBQ=%s; FIL=MicrosoftAccess; DEFAULTDIR=%s"),  _T("SwordBibleDSN"), _T(""),
	csPath	, _T(""));
	mlen = _tcsclen(szDesc);
	for (int i = 0; i < mlen; i++)
	{
		if (szDesc[i] == _T(';'))
			szDesc[i] = _T('\0');
	}
	
	BOOL bOk = SQLConfigDataSource(NULL, ODBC_ADD_DSN, _T("Microsoft Access Driver (*.mdb)\0"), (LPCTSTR)szDesc);
	if(!bOk)
		::AfxMessageBox(_T("Failed"), MB_OK);

	delete [] szDesc;
	return bOk;
	
	
}



//------------------------------------------------------------//
//CreateDatabaseX                                
//Purpose:  create a new Jet database with the Create method
//------------------------------------------------------------//
void CVerseLists::CreateDatabaseX()
{    
    HRESULT hr = S_OK;

    // Define ADOX object pointers.
    // Initialize pointers on define.
    // These are in the ADOX::  namespace.
    
ADOX::_CatalogPtr m_pCatalog = NULL;
ADOX::_TablePtr m_pTable2 = NULL;

CString filepath = _T("");
GetAppDirectory(filepath);
filepath += _T("\\Data.mdb");


    //Set ActiveConnection of Catalog to this string
	TCHAR * pChar = new TCHAR[400];
	memset(pChar, 0, 400);
	_stprintf(pChar, _T("Provider='Microsoft.JET.OLEDB.4.0';Data source = %s"),filepath);

  try
    {//Open the catalog


		TESTHR(hr = m_pCatalog.CreateInstance(__uuidof(ADOX::Catalog)));
        m_pCatalog->Create(pChar);
		delete [] pChar;


				/* add the table for the VerseLists */
				TESTHR(m_pTable2.CreateInstance(__uuidof(ADOX::Table)));
				m_pTable2->PutName(_T("VerseListsTable"));
				m_pTable2->Columns->Append(_T("ListName"),ADOX::adVarWChar,50);
				m_pTable2->Columns->Append(_T("Text"),ADOX::adLongVarWChar,65000);
				m_pCatalog->Tables->Append(
				_variant_t((IDispatch *)m_pTable2));

				////////////////////////////////////////////////////



m_pCatalog->Tables->Refresh();

		m_strCmdText = _T("CREATE UNIQUE INDEX IDX_VLTABLE on VerseListsTable (ListName)");
		ADOCG::_ConnectionPtr con = m_pCatalog->GetActiveConnection();    
		if(con) con->Execute(m_strCmdText.AllocSysString() , NULL, adExecuteNoRecords);



m_pCatalog = NULL;

    }
    catch(_com_error &e)
    {
        // Notify the user of errors if any.
        _bstr_t bstrSource(e.Source());
        _bstr_t bstrDescription(e.Description());
          
        _tprintf(_T("\n\tSource :  %s \n\tdescription : %s \n "),
            bstrSource, bstrDescription);

    }
    catch(...)
    {
        AfxMessageBox(_T("Error occured in CreateDatabaseX...."));
    }

}




void CVerseLists::LoadToolbar()
{
	if (!m_toolbar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD
       | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS
        | CBRS_SIZE_DYNAMIC) ||
       !m_toolbar.LoadToolBar(IDR_TOOLBAR_VERSELISTS))
   {
      TRACE0("Failed to create toolbar\n");
      return;      // fail to create
   }

	   // buttons size: 16
   // bitmaps:
   // enabled - IDB_TOOLBAR_NORMAL
   // hovered - IDB_TOOLBAR_HOT
   // disabled - IDB_TOOLBAR_DISABLED
   // transparent color RGB(255, 0, 255)
   m_toolbar.LoadTCToolBar(24, IDB_TOOLBAR_VERSELISTS,IDB_TOOLBAR_VERSELISTS,
                              IDB_TOOLBAR_VERSELISTS, RGB(255,255,255));

	// Position the control bars using RepositionBars. 
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);

	// Expand the size of the Toolbar window so all controls in it would fit	
	CRect toolBarRect;
	CToolBarCtrl& formatBar = m_toolbar.GetToolBarCtrl();
	formatBar.GetWindowRect(toolBarRect);
	ScreenToClient(toolBarRect);
	toolBarRect.left += 10  ;
	toolBarRect.right += 500  ;
	formatBar.MoveWindow(toolBarRect,TRUE);

	// drop down arrow for edit button
  formatBar.SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); 
  TBBUTTONINFO tbi;
  tbi.dwMask= TBIF_STYLE;
  tbi.cbSize= sizeof(TBBUTTONINFO);
  formatBar.GetButtonInfo(ID_VERSELISTS_EDIT, &tbi);
  tbi.fsStyle |= TBSTYLE_DROPDOWN;
  formatBar.SetButtonInfo(ID_VERSELISTS_EDIT, &tbi);

 	// disable the placeholder button(s)
	for(int n = 0; n < 4; n++) {
	formatBar.SendMessage((UINT) TB_ENABLEBUTTON, (WPARAM)(int) ID_PLACEHOLDER1 + n,  (LPARAM) MAKELONG(FALSE, 0) ); 
	}

	// Setting additional style for the toolbar control
	m_toolbar.ModifyStyle(0, TBSTYLE_FLAT);
}


void CVerseLists::GenerateError(HRESULT hr, PWSTR pwszDescription)
{

    CString strError;

     // Format and display the error message
     strError.Format(_T("Run-time error `%d (%x)'"), hr, hr);
     strError += _T("\n\n");
     strError += pwszDescription;

     AfxMessageBox(strError);


}

void CVerseLists::UpdateBoundData()
{

	if(IsAppData == FALSE)
		return ;    // no point in continuing if database DNE

	try {

	if ( _tcscmp(list_name, m_rsRecSet.m_szVerseCode) != 0 )
      _tcscpy(m_rsRecSet.m_szVerseCode, list_name);

	if ( _tcscmp(data , m_rsRecSet.m_szVerseText)   != 0      )
      _tcscpy(m_rsRecSet.m_szVerseText,data);

	}

	   // Any errors?
     catch (_com_error &e)
     {
		
         // Generate an error message
         GenerateError(e.Error(), e.Description());

	 }

}


/* helper function for adding a new record to access database */
void CVerseLists::AddNew()
{
	if(IsAppData == FALSE)
		return ;    // no point in continuing if database DNE

	try
     {
		  
         // Update the current record
         m_piAdoRecordBinding->Update(&m_rsRecSet);
         // Can we add a new record?
         if (m_pRs->Supports(adAddNew))
         {
			 // Create a blank record
             CreateBlankRecord();
			
             // Add the blank record
             m_piAdoRecordBinding->AddNew(&m_rsRecSet);
             // Move to the last record
			 if(m_pRs->EndOfFile == FALSE)
              m_pRs->MoveLast();

         }
     }
     // Any errors?
     catch (_com_error &e)
     {
         // Generate an error message
	      GenerateError(e.Error(), e.Description());
     }

}


/* helper function that creates an empty record in the MS access database */
void CVerseLists::CreateBlankRecord()
{
	if(IsAppData == FALSE)
		return;    // no point in continuing if database DNE

	try {

    // Create the blank values to be used
     CString strBlank = _T("");

	_tcscpy(m_rsRecSet.m_szVerseCode, (LPCTSTR)strBlank);
  _tcscpy(m_rsRecSet.m_szVerseText, (LPCTSTR)strBlank);
	 
	}

	   // Any errors?
     catch (_com_error &e) // Generate an error message
	 {  GenerateError(e.Error(), e.Description());}

	 



}

/* helper function for deletion of a record from MS access database */
void CVerseLists::Delete()
{
	if(IsAppData == FALSE)
		return ;    // no point in continuing if database DNE

     try
     {
         // Update the current record
         m_piAdoRecordBinding->Update(&m_rsRecSet);
         // Can we delete a record?
         if (m_pRs->Supports(adDelete))
         {
                 // Delete the record
                 m_pRs->Delete(adAffectCurrent);
                 // Move to the previous record
                 m_pRs->MovePrevious();
            
         }



     }
     // Any errors?
     catch (_com_error &e) // Generate an error message
	 {  GenerateError(e.Error(), e.Description());}
}





/* helper function for the MS access database */
void CVerseLists::RefreshBoundData()
{
	if(IsAppData == FALSE)
	return;    // no point in continuing if database DNE
	
	try {

		if (adFldOK == m_rsRecSet.lVerseCodeStatus)
         // Copy the value
        list_name = m_rsRecSet.m_szVerseCode;
     else
         // Otherwise, set the value to 0
         list_name = _T("");


	 	if (adFldOK == m_rsRecSet.lVerseTextStatus)
         // Copy the value
         data.Format(_T("%s"), m_rsRecSet.m_szVerseText);
     else
         // Otherwise, set the value to 0
         data.Format(_T("%s"), _T("") );

	}

	   // Any errors?
     catch (_com_error &e)
     {
		
         // Generate an error message
         GenerateError(e.Error(), e.Description());

	 }

}



bool CVerseLists::GetAppDirectory( CString& strAppDir )
{
   bool bRet        = false;
   DWORD dwSize     = _MAX_PATH + 1;
   LPTSTR pszAppDir = new TCHAR[dwSize];

   if( ::GetModuleFileName( NULL, pszAppDir, dwSize ) )
   {
      strAppDir = pszAppDir;
      int nPos = 0; 
	  nPos = strAppDir.ReverseFind(_T('\\'));
      if( -1 != nPos )
      {
         strAppDir = strAppDir.Left( nPos );
         bRet = true;
      }
   }
   delete []pszAppDir;

   return bRet;

}

/* Add a new list to the VerseLists */
void CVerseLists::OnMakeList()
{
	CCreateList dlg;

	if(dlg.DoModal() == IDOK)
	{
		SaveList(dlg.listName, _T(""), TRUE);  // add to database
	    PopulateTree();
	}

	
}

/* Delete a list from the tree and the database. This deletes all verses associated with that list */
void CVerseLists::OnDeleteList()
{
	 // Get the selected Tree Control item
   HTREEITEM selected = treeCtrl.GetSelectedItem();
   
   // Get the text of the selected item
   CString sel_text = treeCtrl.GetItemText(selected);

   if(sel_text.GetLength() < 2) return;

   CString csConfirm = _T("Are you sure you want to remove this list?");

   if( ::MessageBox(NULL,csConfirm, _T("Remove List"),MB_OKCANCEL) != IDOK)
     return;


   treeCtrl.DeleteItem(selected);  // delete item from the tree

	 // Delete the list name and its contents from the database
	 if (m_pRs->BOF == FALSE)
	 m_pRs->MoveFirst();

	CString tofind;
	CString temp = sel_text;
	temp.Replace(_T("'"),  _T("''")  );
	tofind.Format(_T("ListName='%s'"), temp);
	m_pRs->Find(tofind.AllocSysString() , 0, ADOCG::adSearchForward);    

	RefreshBoundData();

	if(list_name.Compare(sel_text) == 0)  Delete();
	
   if(treeCtrl.GetCount() == 0) m_richCtrl.SetRTF((CString) ""); 
	
}

/* Displays a given verse in the rich edit control */
void CVerseLists::DisplayText()
{

	
	CString display =  CBiblePage::font.GetHeaderRTF();
	sword::SWKey key;

	 // Get the selected Tree Control item
     HTREEITEM selected = treeCtrl.GetSelectedItem();

	CString listStr = treeCtrl.GetItemText(selected);
	CString dataStr = GetListData(listStr);

	
	sword::VerseKey vk;
	lk = vk.ParseVerseList((CStringA) dataStr, vk , TRUE);
	
	for (lk = sword::TOP; !lk.Error(); lk++) 
	{

     //change the key
	key = lk;
	pModuleActive->SetKey(key);

	// display the text
	display = display +   _T("\\cf1\\b ") + (CString) lk.getShortText() + _T(" \\b0\\cf2 ") + 
		 _T(" \\par\\par ");
	
	CString renderText = (CString) pModuleActive->RenderText();
	
	renderText.Replace(_T("\\par"),_T(" "));
	renderText.Replace(_T("\\cf6"),  _T("\\cf9"));
	
	display = display + renderText + _T("\\par\\par ");

	} // <--- done collecting all the verses in string "display"

	display += _T("}");

	// footnote tags are enclosed between href tags in bibles. Footnotes use color cf5 in the color table 
	// x reference tags are enclosed between href tags in bibles. cross references use color cf6 in the color table 
	display.Replace(_T("<a href=\"\">*n"),  _T("\\cf5\\b *n"));
	display.Replace(_T("<a href=\"\">*x"),  _T("\\cf6\\b *x"));
	display.Replace(_T("</a>"), _T("\\cf2\\b0 "));
	display.Replace(_T("<a href=\"\">"),  _T(" "));

	


	m_richCtrl.SetRTF(display);

}


/* Queries the database to retrieve a string of verses for the currently selected list */
CString CVerseLists::GetListData(CString name)
{
	if(IsAppData == FALSE)return _T("");    // no point in continuing if database DNE

	try
	{

	if (m_pRs->BOF == VARIANT_FALSE)
	 m_pRs->MoveFirst();

	CString tofind;
	CString temp = name;
	temp.Replace(_T("'"),  _T("''")  );
	tofind.Format(_T("ListName='%s'"), temp);
	m_pRs->Find(tofind.AllocSysString() , 0, ADOCG::adSearchForward);

	RefreshBoundData();

	if(list_name.Compare(name) == 0)  return data;
	else return _T("");
	 
	 

	}  // end of try block

	   // Any errors?
     catch (_com_error &e)
     {   // Generate an error message
         GenerateError(e.Error(), e.Description());
		 return _T("");
	 }

}





void CVerseLists::AppendBibleVersions()
{

	CMenu *pSubMenu = GetMenu()->GetSubMenu(3); 

      // fill in the strings for different bible versions
	for(int i = 0; i < CSwordBibleDoc::numBiblesAvailable; i++)
		if(CSwordBibleDoc::pRTFBibleModule[i] != NULL)
		{
			
			         pSubMenu->AppendMenu(MF_STRING,ID_BIBLEVERSION_NAMES + i, (CString) CSwordBibleDoc::pRTFBibleModule[i]->Description());
					 if(active == i) pSubMenu->CheckMenuItem(ID_BIBLEVERSION_NAMES + i,MF_CHECKED); 

		}
}

void CVerseLists::OnChangeVersion(UINT nID)
{	
    CMenu *pSubMenu = GetMenu()->GetSubMenu(3); 
    active = int(nID)- int(ID_BIBLEVERSION_NAMES);  // set the new active version
	pModuleActive = CSwordBibleDoc::pRTFBibleModule[active];   // set the active module
     
	for(int i = 0; i < CSwordBibleDoc::numBiblesAvailable; i++)
		if(CSwordBibleDoc::pRTFBibleModule[i] != NULL)
		    if(active == i)			pSubMenu->CheckMenuItem(ID_BIBLEVERSION_NAMES + i,MF_CHECKED); 
			else    				pSubMenu->CheckMenuItem(ID_BIBLEVERSION_NAMES + i,MF_UNCHECKED); 
   
	DisplayText();
}

void CVerseLists::OnToolbarBibleVersions()
{
	RECT rc;
    m_toolbar.GetToolBarCtrl().GetItemRect(0,&rc);
	m_toolbar.ClientToScreen(&rc); // TrackPopupMenu uses screen coords

   CMenu menu;
   menu.CreatePopupMenu();
   CMenu* pPopup = NULL;
   pPopup = &menu;

   if(pPopup == NULL) return;
   int dwSelectionMade;  

 
   // fill in the strings for different bible versions
	for(int i = 0; i < CSwordBibleDoc::numBiblesAvailable; i++)
		if(CSwordBibleDoc::pRTFBibleModule[i] != NULL)
		{
			         pPopup->AppendMenu(MF_STRING,ID_BIBLEVERSION_NAMES + i, (CString) CSwordBibleDoc::pRTFBibleModule[i]->Description());
					 if(active == i) pPopup->CheckMenuItem(ID_BIBLEVERSION_NAMES + i,MF_CHECKED); 

		}


         dwSelectionMade =  pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON|TPM_RETURNCMD, 
         rc.left, rc.bottom + 1, this);

	
		int selection = dwSelectionMade;
		int base = ID_BIBLEVERSION_NAMES;
		int sel= selection - base;


	if(sel >= 0 && sel < CSwordBibleDoc::numBiblesAvailable) 
	{
		active = sel;
		pModuleActive = CSwordBibleDoc::pRTFBibleModule[active];   // set the active module
   		DisplayText();

		// change the check on the "menu" item that shows active bible version
     	for(int i = 0; i < CSwordBibleDoc::numBiblesAvailable; i++)	
		if(active == i) GetMenu()->GetSubMenu(3)->CheckMenuItem(ID_BIBLEVERSION_NAMES + i,MF_CHECKED); 
		else GetMenu()->GetSubMenu(3)->CheckMenuItem(ID_BIBLEVERSION_NAMES + i,MF_UNCHECKED); 

	}
    pPopup->DestroyMenu(); 

}


/* Delete a verse from the list */
void CVerseLists::OnDeleteVerse()
{
    // Get the selected Tree Control item
   HTREEITEM selected = treeCtrl.GetSelectedItem();

   // Get the text of the selected item
   CString sel_text = treeCtrl.GetItemText(selected);

   CRemove dlg;
   dlg.icon = _T("file");
   dlg.type = _T("multiple");
   dlg.arr.RemoveAll();  // empty the array


  sword::VerseKey vk;
	sword::SWKey key;
	CString existing = GetListData(sel_text);
	if(existing == _T("")) return; // no verses contained within the current list
    lk = vk.ParseVerseList((CStringA) existing, vk , TRUE);
	
	for (lk = sword::TOP; !lk.Error(); lk++) 
	{

	key = lk;
	dlg.arr.Add((CString) key.getShortText());
		
	}
    

	if(dlg.DoModal() == IDOK)
	{
		SaveList(sel_text, dlg.modList, FALSE ); // FALSE indicates that current verse list is "overwritten"
        DisplayText();  // fill the rich edit control
	}

}


// fills the tree control by adding the lists and the verses of the lists in a hierarchy

void CVerseLists::PopulateTree()
{
	treeCtrl.SetRedraw(FALSE);

	treeCtrl.DeleteAllItems();

	try{

		 // fill in the tree with the names of the lists
		if (m_pRs->BOF == VARIANT_FALSE) m_pRs->MoveFirst();
	
		HTREEITEM ListName;

		while(VARIANT_FALSE == m_pRs->EndOfFile)
		{
			
			RefreshBoundData();

			// insert the name of the list in the tree control
			if (list_name.GetLength() > 2) 
			ListName = treeCtrl.InsertItem(list_name);

			m_pRs->MoveNext();  // Move to the next list
		}
		
		 }
		 // Any errors?
		 catch (_com_error &e)
		 {
			 // Display the error
			 GenerateError(e.Error(), e.Description());
		 }

		 treeCtrl.SortChildren(NULL);
		treeCtrl.SetRedraw(TRUE);
		treeCtrl.SelectItem( treeCtrl.GetFirstVisibleItem() );

	// end of the void function
	return;


}

void CVerseLists::OnTreeChangeSel(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
	
	DisplayText();

	*pResult = 0;
}

void CVerseLists::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

// resize the tree control
	if( ::IsWindow( treeCtrl.GetSafeHwnd()))
	treeCtrl.SetWindowPos(NULL,10,35, 200 ,cy-45,SWP_NOZORDER | SWP_NOACTIVATE);

	// resize the rich edit control
	if( ::IsWindow( m_richCtrl.GetSafeHwnd()))
	m_richCtrl.SetWindowPos(NULL,220,35, cx - 230 ,cy-45,SWP_NOZORDER | SWP_NOACTIVATE);

}

void CVerseLists::HideVerseLists()
{

	ShowWindow(SW_HIDE);


}

void CVerseLists::OnPrint()
{
	CBibleForm::pControl = (CRichEditCtrl *) GetDlgItem(IDC_RICHEDIT_MEM);
	AfxGetMainWnd()->SendMessageToDescendants(WM_PRINT_CONTROL, (WPARAM) 1 , 0);

}


void CVerseLists::OnNext()
{
	HTREEITEM current = treeCtrl.GetSelectedItem();
	if(current == NULL) return;

	HTREEITEM next = treeCtrl.GetNextSiblingItem(current);

	if(next == NULL)
	{
		HTREEITEM first = treeCtrl.GetRootItem();
		if(first != NULL)   treeCtrl.SelectItem(first);

		 return;
	}

	treeCtrl.SelectItem( next );


}

void CVerseLists::OnPrevious()
{

	HTREEITEM current = treeCtrl.GetSelectedItem();
	if(current == NULL) return;

	HTREEITEM previous = treeCtrl.GetPrevSiblingItem(current);

	if(previous != NULL)
	{
		treeCtrl.SelectItem( previous );
		 return;
	}

}

void CVerseLists::SaveSearchResults()
{

	CSaveVerseRange dlg;


	// fill in the names of existing lists
	try{
		 // fill in the tree with the names of the lists
		if (m_pRs->BOF == VARIANT_FALSE) m_pRs->MoveFirst();
	
		while(VARIANT_FALSE == m_pRs->EndOfFile)
		{			
		RefreshBoundData();
		if (list_name.GetLength() > 2) dlg.ListNames.Add(list_name);
	
		m_pRs->MoveNext();  // Move to the next list
		}
		
		 }
		 // Any errors?
		 catch (_com_error &e)
		 {	 // Display the error
			 GenerateError(e.Error(), e.Description());
		 }


	if(dlg.DoModal() == IDOK)
	{

		// Get a list of verses to add
 	CString vl_data = _T("");
	for (CSearch::searchResults = sword::TOP; !CSearch::searchResults.Error(); CSearch::searchResults++)
		vl_data = vl_data + (CString) CSearch::searchResults + _T(" ;");

	SaveList(dlg.listName, vl_data, TRUE);   // save to database
	                                         // TRUE indicates that if list name already exists,
	                                         // the verses will be appended to it and not over-written
	PopulateTree();

     }
	
}



BOOL CVerseLists::SaveVerseRange(CString vl_data)
{

	CSaveVerseRange dlg;

	// fill in the names of existing lists
	try{
		 // fill in the tree with the names of the lists
		if (m_pRs->BOF == VARIANT_FALSE) m_pRs->MoveFirst();
	
		while(VARIANT_FALSE == m_pRs->EndOfFile)
		{			
		RefreshBoundData();
		if (list_name.GetLength() > 2) dlg.ListNames.Add(list_name);
	
		m_pRs->MoveNext();  // Move to the next list
		}
		
		 }
		 // Any errors?
		 catch (_com_error &e)
		 {	 // Display the error
			 GenerateError(e.Error(), e.Description());
		 }


	if(dlg.DoModal() == IDOK)
	{

		// Get a list of verses to add
 	SaveList(dlg.listName, vl_data, TRUE);   // save to database
	                                         // TRUE indicates that if list name already exists,
	                                         // the verses will be appended to it and not over-written
	PopulateTree();

	return TRUE;

     }

	return FALSE;
	
}


LONG32 CVerseLists::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{

 #define lpnm   ((LPNMHDR)lParam)
 #define lpnmTB ((LPNMTOOLBAR)lParam)

 if(lpnm->code == TBN_DROPDOWN && lpnmTB->iItem == ID_VERSELISTS_EDIT ) { 
   OnEditBtnDropDown();
   return FALSE;   //notification was handled                      
 }

 return CDialog::OnNotify(wParam, lParam, pResult);


}

void CVerseLists::OnEditBtnDropDown()
{
  RECT rc;
  m_toolbar.GetToolBarCtrl().GetItemRect(2,&rc);
  m_toolbar.ClientToScreen(&rc); // TrackPopupMenu uses screen coords

  CMenu menu;
  menu.CreatePopupMenu();
  
  if(menu.GetSafeHmenu() == NULL) return;
  int dwSelectionMade = 0;  
 
  menu.AppendMenu(MF_STRING,ID_EDIT_ADDLIST, _T("Add List") );
  menu.AppendMenu(MF_STRING,ID_VERSELISTS_EDIT, _T("Add Verse") );
  menu.AppendMenu(MF_STRING,ID_EDIT_REMOVELIST, _T("Delete List") );
  menu.AppendMenu(MF_STRING,ID_EDIT_REMOVEVERSE, _T("Delete Verse") );
   
  dwSelectionMade =  menu.TrackPopupMenu(TPM_RIGHTALIGN | TPM_LEFTBUTTON|TPM_RETURNCMD, 
         rc.right, rc.bottom + 1, this);

  SendMessage(WM_COMMAND, dwSelectionMade);   
}



